import 'dart:convert';
import 'package:flutter_common_utils/src/app_utils.dart';
import 'package:path_provider/path_provider.dart';

import '../log/log.dart';
import 'i_store.dart';
import 'package:mmkv/mmkv.dart';

class MMKVStore extends IStore {
  late MMKV _kv;
  final Map<String, MMKV> _map = {};
  final MMKVMode? mode;
  String? _rootDir;

  MMKVStore({this.mode, String? rootDir}) {
    this._rootDir = rootDir;
  }

  @override
  Future<void> init() async {
    if (_rootDir == null && AppUtils.isDesktop) {
      final mmkvPath = (await getApplicationSupportDirectory()).path;
      _rootDir = AppUtils.isWindows ? "$mmkvPath\\mmkv" : "$mmkvPath/mmkv";
    }
    final result = await MMKV.initialize(rootDir: _rootDir);
    LogUtils.d('MMKV for flutter with rootDir = $result');
    _kv = mode != null ? MMKV("", mode: mode!) : MMKV.defaultMMKV();
  }

  MMKV getKv({String? name}) {
    if (name == null || name.isEmpty) {
      return _kv;
    }
    if (_map.containsKey(name)) {
      return _map[name]!;
    } else {
      MMKV kv = mode != null ? MMKV(name, mode: mode!) : MMKV(name);
      _map[name] = kv;
      return kv;
    }
  }

  @override
  String getString(String key, {String def = "", String? name}) {
    return getKv(name: name).decodeString(key) ?? def;
  }

  @override
  bool setString(String key, String value, {String? name}) {
    return setAny(key, value, name: name);
  }

  @override
  int getInt(String key, {int def = 0, String? name}) {
    return getKv(name: name).decodeInt(key, defaultValue: def);
  }

  @override
  bool setInt(String key, int value, {String? name}) {
    return setAny(key, value, name: name);
  }

  @override
  double getDouble(String key, {double def = 0, String? name}) {
    return getKv(name: name).decodeDouble(key, defaultValue: def);
  }

  @override
  bool setDouble(String key, double value, {String? name}) {
    return setAny(key, value, name: name);
  }

  bool getBool(String key, {bool def = false, String? name}) {
    return getKv(name: name).decodeBool(key, defaultValue: def);
  }

  @override
  bool setBool(String key, bool value, {String? name}) {
    return setAny(key, value, name: name);
  }

  @override
  List<String> getStringList(String key, {List<String> def = const [], String? name}) {
    return _getJson(key, name: name) ?? def;
  }

  @override
  bool setStringList(String key, List<String> value, {String? name}) {
    return setAny(key, value, name: name);
  }

  @override
  Map getMap(String key, {Map def = const {}, String? name}) {
    return _getJson(key, name: name) ?? def;
  }

  @override
  bool setMap(String key, Map value, {String? name}) {
    return setAny(key, value, name: name);
  }

  /// 批量设置键值对
  bool setAny(String key, dynamic value, {String? name}) {
    try {
      if (value is String) {
        return getKv(name: name).encodeString(key, value);
      } else if (value is int) {
        return getKv(name: name).encodeInt(key, value);
      } else if (value is double) {
        return getKv(name: name).encodeDouble(key, value);
      } else if (value is bool) {
        return getKv(name: name).encodeBool(key, value);
      } else {
        // 对于所有其他类型，尝试将它们转换为 JSON 字符串
        try {
          final jsonString = jsonEncode(value);
          return getKv(name: name).encodeString(key, jsonString);
        } catch (e) {
          throw UnsupportedError('Failed to convert to JSON: ${value.runtimeType}');
        }
      }
    } catch (e) {
      throw UnsupportedError('Failed to mmkv  ${value.runtimeType},$e');
    }
  }

  @override
  bool contains(String key, {String? name}) {
    return getKv(name: name).containsKey(key);
  }

  @override
  bool remove(String key, {String? name}) {
    getKv(name: name).removeValue(key);
    return true;
  }

  @override
  void clear({String? name}) {
    getKv(name: name).clearAll();
  }

  //获取json形式
  dynamic _getJson(String key, {String? name}) {
    if (contains(key, name: name)) {
      final value = getString(key);
      try {
        return jsonDecode(value);
      } catch (e) {
        return null;
      }
    } else {
      return null;
    }
  }
}
